package com.ming.common.liteflow.core.el;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.ming.common.liteflow.core.el.bus.ELBusIf;
import com.ming.common.liteflow.core.el.bus.ELBusNode;
import com.ming.common.liteflow.core.el.bus.ELBusSwitch;
import com.ming.common.liteflow.core.graph.EdgeProperties;
import com.ming.common.liteflow.core.graph.GraphEL;
import com.ming.common.liteflow.core.graph.Node;
import com.ming.common.liteflow.core.node.NodeInfoWrapper;
import com.ming.common.liteflow.core.execption.LiteFlowELException;
import com.yomahub.liteflow.builder.el.*;
import com.yomahub.liteflow.enums.NodeTypeEnum;

import java.util.*;
import java.util.stream.Collectors;

public class FlowConvertEL {

    public static ELWrapper logicFlow(GraphEL graphEL) throws LiteFlowELException {
        ELWrapper wrapper = logicFlowWrapper(graphEL);
        if(wrapper instanceof ThenELWrapper){
            ThenELWrapper thenELWrapper = (ThenELWrapper) wrapper;
            Object[] preArray = FlowConvertELUtil.nodeListToArray(graphEL.getPreList(), graphEL);
            if(preArray != null){
                thenELWrapper.pre(preArray);
            }
            Object[] finallyArray = FlowConvertELUtil.nodeListToArray(graphEL.getFinallyList(), graphEL);
            if(finallyArray != null) {
                thenELWrapper.finallyOpt(finallyArray);
            }
        }
        return wrapper;
    }

    public static ELWrapper logicFlowWrapper(GraphEL graphEL) throws LiteFlowELException {
        //单起点嵌套处理
        ELWrapper wrapper = ELBus.then();
        Node startNode = graphEL.getStartNode();
        List<Node> endNode = graphEL.getEndNode();
        List<Node> startNodeList = graphEL.getStartNodeList();
        if(CollUtil.isNotEmpty(startNodeList) && startNodeList.size() > 1){
            flowMultiple(wrapper, startNodeList, null, graphEL);
        }else if(endNode != null && endNode.size() > 1){
            flowSingle(wrapper, startNode, null, graphEL, 0,null,null);
        }else{
            flowSingle(wrapper, startNode, endNode == null ? null : endNode.get(0), graphEL, 0,null,null);
        }
        return wrapper;
    }

    // 多起点处理
    public static void flowMultiple(ELWrapper wrapper, List<Node> startNodeList, Node endNode, GraphEL graphEL) throws LiteFlowELException {
        Node joinNode = graphEL.getJoinNode(startNodeList);
        WhenELWrapper whenELWrapper = ELBus.when();
        for (Node startNode : startNodeList){
            ThenELWrapper thenELWrapper = ELBus.then();
            flowSingle(thenELWrapper,startNode,endNode,graphEL, 0,null,null);
            whenELWrapper.when(thenELWrapper);
        }
        FlowConvertELUtil.convert(wrapper,whenELWrapper);
        if(endNode == null){
            flowSingle(wrapper,joinNode,endNode,graphEL, 0,null,null);
        }
    }

    // 单起点处理
    public static void flowSingle(ELWrapper wrapper, Node currNode, Node endNode, GraphEL graphEL, int flag, List<Node> excludeList,List<Node> excludeNodeList) throws LiteFlowELException {
        Map<Node, List<Node>> nodeMap = graphEL.getList();
        if(currNode == null && endNode == null){
            return;
        }
        if(graphEL.isEndNode(currNode) || graphEL.isEndNode(currNode, endNode)){
            flowThen(wrapper,currNode,currNode,graphEL);
            return;
        }
        // 多并行路径嵌套
        List<Node> nodeList = nodeMap.get(currNode);
        if(nodeList.size() == 1 && graphEL.getReverseList().get(nodeList.get(0)).size() > 1){
            flowThen(wrapper,currNode,currNode,graphEL);
            if(flag == 1){
                flowSingle(wrapper,endNode,null,graphEL, 0,excludeList, excludeNodeList);
            }
            return;
        }
        Node forkNode = graphEL.getForkNode(currNode);
        List<Node> forkChildList = nodeMap.get(forkNode);

        if(graphEL.isCommonEdge(currNode)){
            if(currNode == forkNode && forkChildList.size() > 1){
//                flowForkGroup(wrapper, forkNode, endNode, forkNode, graphEL, forkChildList, excludeList, excludeNodeList);
                flowForks(wrapper, forkNode, endNode, graphEL, forkChildList, excludeList, excludeNodeList);
                return;
            }else if(graphEL.isThen(currNode,forkNode)){
                flowThen(wrapper,currNode,forkNode,graphEL);
            }else if(graphEL.isWhen(currNode,forkNode)){
                flowWhen(wrapper,currNode,forkNode,graphEL,3);
            }
        }else{
            if(NodeTypeEnum.SWITCH.getCode().equalsIgnoreCase(currNode.getType()) || NodeTypeEnum.SWITCH_SCRIPT.getCode().equals(currNode.getProperties().getType())){
                flowSwitch(wrapper,currNode,currNode,graphEL,forkChildList);
                return;
            }else if(NodeTypeEnum.IF.getCode().equalsIgnoreCase(currNode.getType()) || NodeTypeEnum.IF_SCRIPT.getCode().equals(currNode.getProperties().getType())){
                flowIf(wrapper,currNode,endNode,graphEL,forkChildList);
                return;
            }
        }
        flowSingle(wrapper, forkNode, endNode, graphEL, 0,excludeList, excludeNodeList);
    }

    // 分叉处理
    public static void flowForks(ELWrapper wrapper, Node currNode, Node endNode, GraphEL graphEL, List<Node> forkChildList, List<Node> excludeList, List<Node> excludeNodeList) throws LiteFlowELException {
        flowThen(wrapper,currNode,currNode,graphEL);
        Node joinNode = graphEL.getJoinNode(forkChildList);

        List<List<Node>> allPaths = graphEL.getAllPaths(currNode, joinNode, false);
        if(CollUtil.isEmpty(allPaths)){
            allPaths = graphEL.getAllPaths(currNode, false);
        }
        Map<Node, List<List<Node>>> groupedPaths1 = allPaths.stream().collect(Collectors.groupingBy(path -> path.get(path.size() - 1)));
        Map<Node, List<List<Node>>> groupedPaths2 = allPaths.stream().collect(Collectors.groupingBy(path -> path.get(0)));

        boolean isComplexPath = groupedPaths1.size() == 1 && groupedPaths1.size() == groupedPaths2.size();
        if(joinNode != null && !isComplexPath){
            flowMultiple(wrapper,forkChildList,joinNode,graphEL);
            Node prevJoinNode = graphEL.prevJoinNode(joinNode);
            if(prevJoinNode == currNode){
                //List<List<Node>> allPaths = graphEL.getAllPaths(currNode, false);
                Node prevSameNode = FlowConvertELUtil.getPrevSameNode(null, allPaths);
                flowSingle(wrapper, prevSameNode, null, graphEL, 0,excludeList, excludeNodeList);
            }
        }else {
            //List<List<Node>> allPaths = graphEL.getAllPaths(currNode, false);
            //Map<Node, List<List<Node>>> groupedPaths1 = allPaths.stream().collect(Collectors.groupingBy(path -> path.get(path.size() - 1)));
            //Map<Node, List<List<Node>>> groupedPaths2 = allPaths.stream().collect(Collectors.groupingBy(path -> path.get(0)));
            List<Node> forkStartNodeList = FlowConvertELUtil.getPrevForkNode(currNode, groupedPaths1, graphEL);
            if (allPaths.size() == groupedPaths1.size()) {
                WhenELWrapper whenELWrapper = ELBus.when();
                for (Node child : forkChildList) {
                    ThenELWrapper thenELWrapper = ELBus.then();
                    flowSingle(thenELWrapper, child, null, graphEL, 0, null, null);
                    boolean isThen = graphEL.isThen(child);
                    List<Node> nodeList = graphEL.getList().get(child);
                    boolean hasCommonElements = FlowConvertELUtil.isPathSameNode(groupedPaths2,child);
                    if (isThen && nodeList.size() == 1 && hasCommonElements) {
                        flowSingle(thenELWrapper, nodeList.get(0), null, graphEL, 0, null, null);
                    }
                    whenELWrapper.when(thenELWrapper);
                }
                graphEL.setGroupNodeProp(currNode,whenELWrapper);
                FlowConvertELUtil.convert(wrapper, whenELWrapper);
            } else if (forkStartNodeList.size() > 1) {// 多分叉起点 有交集路径
                WhenELWrapper whenELWrapper = ELBus.when();
                for (Node child : forkChildList) {
                    ThenELWrapper thenELWrapper = ELBus.then();
                    flowSingle(thenELWrapper, child, null, graphEL, 0, null, null);
                    boolean isThen = graphEL.isThen(child);
                    List<Node> nodeList = graphEL.getList().get(child);
                    if (isThen && nodeList.size() == 1) {
                        flowSingle(thenELWrapper, nodeList.get(0), null, graphEL, 0, null, null);
                    }
                    whenELWrapper.when(thenELWrapper);
                }
                graphEL.setGroupNodeProp(currNode,whenELWrapper);
                FlowConvertELUtil.convert(wrapper, whenELWrapper);
            } else if (forkStartNodeList.size() == 1) {// 单分叉起点 有交集路径
                WhenELWrapper whenELWrapper = ELBus.when();
                for (Map.Entry<Node, List<List<Node>>> group : groupedPaths1.entrySet()){
                    List<Node> excludeLists = groupedPaths1.entrySet().stream()
                            .filter(entry -> !entry.getKey().equals(group.getKey()))
                            .flatMap(entry -> entry.getValue().stream().map(subList -> subList.get(0))).distinct().collect(Collectors.toList());
                    List<Node> includeList = group.getValue().stream().map(m -> m.get(0)).collect(Collectors.toList());
                    if(CollUtil.isNotEmpty(includeList)){
                        excludeLists.removeAll(includeList);
                    }
                    ThenELWrapper thenELWrapper2 = ELBus.then();
                    List<Node> forkChildLists = forkChildList.stream().filter(m-> !excludeLists.contains(m)).collect(Collectors.toList());
                    if(forkChildLists.size() == 1){
                        flowSingle(thenELWrapper2,forkChildLists.get(0),endNode,graphEL, 0,excludeList,excludeNodeList);
                    }else{
                        flowMultiple(thenELWrapper2,forkChildLists,endNode,graphEL);
                    }
                    whenELWrapper.when(thenELWrapper2);
                }
                graphEL.setGroupNodeProp(currNode,whenELWrapper);
                FlowConvertELUtil.convert(wrapper, whenELWrapper);
            } else {
                WhenELWrapper whenELWrapper = ELBus.when();
                for (Node child : forkChildList) {
                    ThenELWrapper thenELWrapper = ELBus.then();
                    boolean isThen = graphEL.isThen(child,joinNode);
                    if(isThen){
                        flowThen(thenELWrapper, child, joinNode, graphEL);
                    }else{
                        flowSingle(thenELWrapper, child, joinNode, graphEL, 0, null, null);
                    }
                    whenELWrapper.when(thenELWrapper);
                }
                FlowConvertELUtil.convert(wrapper, whenELWrapper);
                graphEL.setGroupNodeProp(currNode,whenELWrapper);
                Node prevJoinNode = graphEL.prevJoinNode(joinNode);
                if(prevJoinNode == currNode){
                    Node prevSameNode = FlowConvertELUtil.getPrevSameNode(null, allPaths);
                    flowSingle(wrapper, prevSameNode, null, graphEL, 0,excludeList, excludeNodeList);
                }
            }
        }
    }

   /* public static void flowForkGroup(ELWrapper wrapper, Node currNode, Node endNode, Node forkNode, GraphEL graphEL, List<Node> forkChildList, List<Node> excludeList, List<Node> excludeNodeList) throws LiteFlowELException {
        endNode = graphEL.getJoinNode(forkNode, excludeList);
        List<List<Node>> allPaths = graphEL.getAllPaths(forkNode, endNode, false);
        if(allPaths.size() == 0){
            allPaths = graphEL.getAllPaths(forkNode,false);
        }
        if(CollUtil.isNotEmpty(excludeNodeList)){
            allPaths = FlowConvertELUtil.filterPaths(allPaths, excludeNodeList);
        }

        Map<String, List<List<Node>>> resultMap = FlowConvertELUtil.removeLastSameNode(currNode, allPaths);
        allPaths = resultMap.get("noSame");

        List<Node> excludeList2 = resultMap.get("same").stream()
                .flatMap(List::stream) // 将列表展平
                .distinct() // 去重
                .collect(Collectors.toList());

        Map<Node, List<List<Node>>> groupedPaths1 = allPaths.stream().collect(Collectors.groupingBy(path -> path.get(path.size() - 1)));
        Map<Node, List<List<Node>>> groupedPaths2 = allPaths.stream().collect(Collectors.groupingBy(path -> path.get(0)));

        flowThen(wrapper,currNode,currNode,graphEL);

        // 找到分叉起点
        List<Node> forkStartNodeList = FlowConvertELUtil.getPrevForkNode(currNode,groupedPaths1, graphEL);

        // 无交集路径
        if(allPaths.size() == groupedPaths1.size()){
            if(allPaths.stream().map(m -> m.get(m.size() - 1)).distinct().count() == 1){
                // 完全闭合路径
                flowFork(wrapper,currNode,endNode,forkNode,graphEL,forkChildList, excludeList,excludeNodeList, allPaths);
                return;
            }
            WhenELWrapper whenELWrapper = ELBus.when();
            for (Map.Entry<Node, List<List<Node>>> group : groupedPaths2.entrySet()){
                ThenELWrapper thenELWrapper2 = ELBus.then();
                List<Node> lastNodeList = group.getValue().stream().map(m -> m.get(m.size() - 1)).collect(Collectors.toList());
                if(lastNodeList.size() == 1){
                    flowSingle(thenELWrapper2,group.getKey(),lastNodeList.get(0),graphEL,1,excludeList,excludeList2);
                }else{
                    flowSingle(thenELWrapper2,group.getKey(),endNode,graphEL,0,excludeList,excludeList2);
                }
                whenELWrapper.when(thenELWrapper2);
            }
            FlowConvertELUtil.convert(wrapper, whenELWrapper);
            return;
        }

        if(forkStartNodeList.size() > 1){// 多分叉起点 有交集路径
            WhenELWrapper whenELWrapper = ELBus.when();
            for (Map.Entry<Node, List<List<Node>>> group : groupedPaths2.entrySet()){
                ThenELWrapper thenELWrapper2 = ELBus.then();
                List<Node> lastNodeList = group.getValue().stream().map(m -> m.get(m.size() - 1)).collect(Collectors.toList());
                if(lastNodeList.size() == 1){
                    flowSingle(thenELWrapper2,group.getKey(),lastNodeList.get(0),graphEL,1,excludeList,excludeList2);
                }else{
                    flowSingle(thenELWrapper2,group.getKey(),endNode,graphEL,0,excludeList,excludeList2);
                }
                whenELWrapper.when(thenELWrapper2);
            }
            FlowConvertELUtil.convert(wrapper, whenELWrapper);
        }else if(forkStartNodeList.size() == 1 *//*groupedPaths1.size() > 1*//*){// 单分叉起点 有交集路径
            WhenELWrapper whenELWrapper = ELBus.when();
            for (Map.Entry<Node, List<List<Node>>> group : groupedPaths1.entrySet()){
                List<Node> excludeLists = groupedPaths1.entrySet().stream()
                        .filter(entry -> !entry.getKey().equals(group.getKey()))
                        .flatMap(entry -> entry.getValue().stream().map(subList -> subList.get(0))).distinct().collect(Collectors.toList());
                List<Node> includeList = group.getValue().stream().map(m -> m.get(0)).collect(Collectors.toList());
                if(CollUtil.isNotEmpty(includeList)){
                    excludeLists.removeAll(includeList);
                }
                ThenELWrapper thenELWrapper2 = ELBus.then();
                List<Node> forkChildLists = forkChildList.stream().filter(m-> !excludeLists.contains(m)).collect(Collectors.toList());
                if(forkChildLists.size() == 1){
                    flowSingle(thenELWrapper2,forkChildLists.get(0),endNode,graphEL, 0,excludeList,excludeList2);
                }else{
                    flowMultiple(thenELWrapper2,forkChildLists,endNode,graphEL);
                }
                whenELWrapper.when(thenELWrapper2);
            }
            FlowConvertELUtil.convert(wrapper, whenELWrapper);
        }else{ // 完全闭合路径
            flowFork(wrapper,currNode,endNode,forkNode,graphEL,forkChildList, excludeList, excludeNodeList, allPaths);
        }
    }*/

    /*public static void flowFork(ELWrapper wrapper, Node currNode, Node endNode, Node forkNode, GraphEL graphEL, List<Node> forkChildList, List<Node> excludeList,List<Node> excludeNodeList, List<List<Node>> allPaths) throws LiteFlowELException {
        WhenELWrapper whenELWrapper = ELBus.when();
        for (Node child : forkChildList){
            List<List<Node>> containsList = allPaths.stream().filter(m -> m.contains(child)).collect(Collectors.toList());
            ELWrapper thenELWrapper = ELBus.then();
            if(CollUtil.isNotEmpty(containsList)){
                List<Node> containsList2 = containsList.get(0);
                Node eNode = containsList2.get(containsList2.size() - 1);
                flowSingle(thenELWrapper, child, eNode, graphEL, 0,excludeList,excludeNodeList);
            }else{
                flowSingle(thenELWrapper, child, endNode, graphEL, 0,excludeList,excludeNodeList);
            }
            whenELWrapper.when(thenELWrapper);
        }
        graphEL.setGroupNodeProp(currNode,whenELWrapper);
        FlowConvertELUtil.convert(wrapper,whenELWrapper);
        if(endNode == null){
            return;
        }else{
            forkNode = endNode;
            endNode = null;
        }
        flowSingle(wrapper, forkNode, endNode, graphEL, 0,excludeList,excludeNodeList);
    }*/

    // if处理
    public static void flowIf(ELWrapper wrapper, Node startNode, Node endNode, GraphEL graphEL,List<Node> childList) throws LiteFlowELException {
        List<ThenELWrapper> trueList = new ArrayList<>();
        List<ThenELWrapper> falseList = new ArrayList<>();
        for (Node child : childList){
            EdgeProperties edgeProperties = graphEL.getEdgeProperties(startNode, child);
            ThenELWrapper thenELWrapper = ELBus.then();
            flowSingle(thenELWrapper, child, endNode, graphEL, 0,null,null);
            if("true".equalsIgnoreCase(edgeProperties.getIfType())){
                trueList.add(thenELWrapper);
            }else {
                falseList.add(thenELWrapper);
            }
        }
        NodeInfoWrapper properties = startNode.getProperties();
        int trueSize = trueList.size();
        int falseSize = falseList.size();
        if(trueSize > 1){
            throw new LiteFlowELException("if组件不能有多个true节点！");
        }else if(trueSize == 1){
            properties.setCmpTrueOptEL(trueList.get(0));
        }
        if (falseSize > 1) {
            throw new LiteFlowELException("if组件不能有多个false节点！");
        } else if(falseSize == 1){
            properties.setCmpFalseOptEL(falseList.get(0));
        }
        ELWrapper elWrapper = ELBusIf.NEW().node(properties).toELWrapper();
        FlowConvertELUtil.convert(wrapper,elWrapper);
        Node joinNode = graphEL.getJoinNode(startNode);
        if(joinNode != null ){
            flowSingle(wrapper,joinNode,endNode,graphEL, 0,null,null);
        }
    }

    // switch处理
    public static void flowSwitch(ELWrapper wrapper, Node startNode, Node endNode, GraphEL graphEL,List<Node> childList) throws LiteFlowELException {
        List<ThenELWrapper> switchList = new ArrayList<>();
        List<ThenELWrapper> defaultList = new ArrayList<>();
        for (Node child : childList){
            EdgeProperties edgeProperties = graphEL.getEdgeProperties(startNode, child);
            if(edgeProperties != null && "default".equals(edgeProperties.getSwitchType())){
                ThenELWrapper defaultThenELWrapper = ELBus.then();
                defaultThenELWrapper.then(ELBusNode.NEW().node(child.getProperties()).toELWrapper());
                defaultList.add(defaultThenELWrapper);
                continue;
            }
            List<Node> currNodeList = null;
            List<List<Node>> allPaths = graphEL.getAllPaths(startNode);
            for (List<Node> nodeList : allPaths){
                if(nodeList.contains(child)){
                    currNodeList = nodeList;
                    break;
                }
            }
            currNodeList.remove(endNode);
            Node prevNode = currNodeList.get(currNodeList.size() - 1);
            ThenELWrapper thenELWrapper = ELBus.then();
            if(child == prevNode){
                flowThen(thenELWrapper, child, prevNode, graphEL);
            }else{
                flowSingle(thenELWrapper, child, prevNode, graphEL, 0,null,null);
            }
            if(edgeProperties != null){
                if(StrUtil.isNotBlank(edgeProperties.getId())){
                    thenELWrapper.id(edgeProperties.getId());
                }
                if(StrUtil.isNotBlank(edgeProperties.getTag())){
                    thenELWrapper.tag(edgeProperties.getTag());
                }
            }else{
                thenELWrapper.id(child.getProperties().getComponentId());
            }
            switchList.add(thenELWrapper);
        }
        NodeInfoWrapper properties = startNode.getProperties();
        if(defaultList.size() > 1){
            throw new LiteFlowELException("switch组件不能有多个default节点！");
        } else if (defaultList.size() == 1) {
            properties.setCmpDefaultOptEL(defaultList.get(0));
        }
        properties.setCmpToEL(switchList);
        ELWrapper elWrapper = ELBusSwitch.NEW().node(properties).toELWrapper();
        FlowConvertELUtil.convert(wrapper,elWrapper);
    }

    // 串行处理
    public static void flowThen(ELWrapper wrapper, Node startNode, Node endNode, GraphEL graphEL) throws LiteFlowELException {
        List<Node> list = FlowConvertELUtil.getStartEndNode(startNode, endNode, graphEL);
        if(startNode != endNode){
            list.remove(endNode);
        }
        FlowConvertELUtil.convert(wrapper,FlowConvertELUtil.nodeListToArray(list, graphEL));
    }

    // 并行处理
    public static void flowWhen(ELWrapper wrapper, Node startNode, Node endNode, GraphEL graphEL,int excludeStartAndEnd) throws LiteFlowELException {
        if(startNode == graphEL.getStartNode() || graphEL.isXNode(startNode)){
            FlowConvertELUtil.convert(wrapper,ELBus.then(FlowConvertELUtil.getELWrapper(startNode,graphEL)));
        }
        List<List<Node>> allPaths = graphEL.getAllPaths(startNode, endNode, excludeStartAndEnd);
        Node joinNode = graphEL.getJoinNode(startNode);
        List<List<List<Node>>> groupList = graphEL.handlerPaths(allPaths);
        WhenELWrapper whenELWrapper = ELBus.when();
        for (List<List<Node>> listList : groupList){
            if(listList.size() == 1){
                whenELWrapper.when(ELBus.then(FlowConvertELUtil.nodeListToArray(listList.get(0),graphEL)));
            }else{
                ThenELWrapper thenELWrapper = ELBus.then();
                flowSingle(thenELWrapper, listList.get(0).get(0), joinNode, graphEL, 0,null,null);
                whenELWrapper.when(thenELWrapper);
            }
        }
        graphEL.setGroupNodeProp(startNode,whenELWrapper);
        FlowConvertELUtil.convert(wrapper,whenELWrapper);
    }

}
